package com.knowswift.issues.controller;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.api.R;
import com.knowswift.common.bean.issue.po.MonitorDevice;
import com.knowswift.common.bean.issue.po.MonitorRecord;
import com.knowswift.common.bean.issue.vo.*;
import com.knowswift.common.bean.project.po.Module;
import com.knowswift.common.bean.project.po.MonitorSetting;
import com.knowswift.common.bean.project.po.Project;
import com.knowswift.common.bean.project.vo.ModulePermissionVO;
import com.knowswift.common.bean.project.vo.ModuleVO;
import com.knowswift.common.bean.project.vo.MonitorSettingVO;
import com.knowswift.common.bean.project.vo.ProjectLimit;
import com.knowswift.common.common.*;
import com.knowswift.common.utils.ListUtil;
import com.knowswift.common.utils.WrappedBeanCopier;
import com.knowswift.issues.mqtt.MqttMessageUtil;
import com.knowswift.issues.mqtt.message.SetDeviceIntervalMessageForSend;
import com.knowswift.issues.util.MonitorUtil;
import com.knowswift.log.aop.AopLog;
import com.knowswift.security.account.User;
import com.knowswift.security.account.UserService;
import com.knowswift.security.util.AllowVisitor;
import org.apache.commons.lang.BooleanUtils;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@RestController
@RequestMapping("/admin/monitor/")
public class MonitorController extends AbstractController {
    @Resource
    private MqttClient client;

    @PostMapping("/saveSetting")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @AopLog(name = "保存监控设置", platform = "管理后台")
    public BaseResponse saveSetting(@RequestBody @Validated SaveMonitorSettingParam param) {
        monitorSettingService.remove(new LambdaQueryWrapper<MonitorSetting>()
                .ne(MonitorSetting::getName, GlobalProperty.SETTING_NAME_ENABLE));
        MonitorSetting temperature = new MonitorSetting();
        temperature.setName(GlobalProperty.SETTING_NAME_TEMPERATURE);
        if (param.getTemperatureMin() != null && param.getTemperatureMax() != null) {
            Assert.isFalse(param.getTemperatureMin() >= param.getTemperatureMax(), "温度最小值大于或等于最大值");
        }
        temperature.setMin(param.getTemperatureMin());
        temperature.setMax(param.getTemperatureMax());
        monitorSettingService.save(temperature);

        MonitorSetting humidity = new MonitorSetting();
        humidity.setName(GlobalProperty.SETTING_NAME_HUMIDITY);
        if (param.getHumidityMin() != null && param.getHumidityMax() != null) {
            Assert.isFalse(param.getHumidityMin() >= param.getHumidityMax(), "湿度最小值大于或等于最大值");
        }
        humidity.setMin(param.getHumidityMin());
        humidity.setMax(param.getHumidityMax());
        monitorSettingService.save(humidity);

        MonitorSetting electric = new MonitorSetting();
        electric.setName(GlobalProperty.SETTING_NAME_ELECTRICITY);
        if (param.getElectricityMin() != null && param.getElectricityMax() != null) {
            Assert.isFalse(param.getElectricityMin() >= param.getElectricityMax(), "电流最小值大于或等于最大值");
        }
        electric.setMin(param.getElectricityMin());
        electric.setMax(param.getElectricityMax());
        monitorSettingService.save(electric);

        return BaseResponse.success();
    }

    @RequestMapping("/getSetting")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public BaseResponse getSetting() {
        List<MonitorSetting> list = monitorSettingService.list();
        MonitorSettingVO monitorSettingVO = MonitorUtil.getSetting(list);
//        MonitorSetting enable = temp.get(GlobalProperty.SETTING_NAME_ENABLE);
//        monitorSettingVO.setEnable(false);
//        if (enable != null) {
//            if (enable.getEnable() == 1) {
//                monitorSettingVO.setEnable(true);
//            }
//        } else {
//            enable = new MonitorSetting();
//            enable.setName(GlobalProperty.SETTING_NAME_ENABLE);
//            enable.setEnable(0); // 默认关闭
//            monitorSettingService.save(enable);
//        }

        return BaseResponse.success(monitorSettingVO);
    }

    @GetMapping("/enableMonitorAlert")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public BaseResponse enableMonitorAlert(Integer setType) {
        updateEnableStatus(1,setType);
        return BaseResponse.success();
    }

    @GetMapping("/disableMonitorAlert")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public BaseResponse disableMonitorAlert(Integer setType) {
        updateEnableStatus(0,setType);
        return BaseResponse.success();
    }

    private void updateEnableStatus(Integer status,Integer setType) {

        String name;
        switch (setType){
            case 0: name = "temperature";break;
            case 1: name = "humidity";break;
            default: name = "electricity";break;
        }

        MonitorSetting enable = monitorSettingService.getOne(new LambdaQueryWrapper<MonitorSetting>().eq(MonitorSetting::getName, name), false);
        if (enable != null) {
            enable.setInvocation(status);
        } else {
            enable = new MonitorSetting();
            enable.setName(name);
            enable.setEnable(status);
        }
        monitorSettingService.saveOrUpdate(enable);
    };


    @PostMapping("/device/save")
    @PreAuthorize("@userService.checkAuthority('editMonitor')")
    public BaseResponse saveDevice(@RequestBody @Validated SaveMonitorDeviceParam param) {
        int count = monitorDeviceService.count(new LambdaQueryWrapper<MonitorDevice>().eq(MonitorDevice::getSerialNumber, param.getSerialNumber()));
        Assert.isFalse(count != 0, "设备序列号已存在");

        validateDeviceData(param.getModuleName(), param.getProjectName(), param.getAreaName());
        MonitorDevice monitorDevice = WrappedBeanCopier.copyProperties(param, MonitorDevice.class);
        if (param.getAcceptPosition() == null) {
            param.setAcceptPosition(false);
        }
        monitorDevice.setDeviceStatus(GlobalProperty.DEVICE_STATUS_NORMAL);
        monitorDeviceService.save(monitorDevice);
        return BaseResponse.success();
    }

    @PostMapping("/device/update")
    @PreAuthorize("@userService.checkAuthority('editMonitor')")
    public BaseResponse updateDevice(@RequestBody @Validated UpdateMonitorDeviceParam param) {
        validateDeviceData(param.getModuleName(), param.getProjectName(), param.getAreaName());
        MonitorDevice monitorDevice = WrappedBeanCopier.copyProperties(param, MonitorDevice.class);
        Assert.isTrue(monitorDeviceService.updateById(monitorDevice), "设备不存在");
        return BaseResponse.success();
    }


    @PostMapping("/device/get")
//    @PreAuthorize("@userService.checkAuthority('monitorDevices')")
    public BaseResponse get(@RequestBody MonitorDeviceVO param, Authentication authentication) {
        String id = param.getDeviceId();
        Assert.isTrue(id != null, "id不可为空");
        MonitorDevice monitorDevice = monitorDeviceService.getById(id);
        if (monitorDevice == null) {
            return BaseResponse.success();
        }
        MonitorDeviceAlertCountVO vo = WrappedBeanCopier.copyProperties(monitorDevice, MonitorDeviceAlertCountVO.class);

        User user = (User) authentication.getPrincipal();

        boolean allIssues = UserService.checkAuthority(user, "allIssues");
        boolean ownIssues = UserService.checkAuthority(user, "ownIssues");
        boolean projectIssues = UserService.checkAuthority(user, "projectIssues");
        Page<ModuleVO> page = new Page<>(1L, 9999L);
        boolean hasPermission = false;


        if (!allIssues && !ownIssues && projectIssues) {
            // 根据模块和项目判断
            Page<ModulePermissionVO> modulePage = modulePermissionService.getBaseMapper().getByUserIdPage(user.getUserId(), page);
            modulePermissionService.concatData(modulePage.getRecords());
            for (ModulePermissionVO modulePermissionVO : modulePage.getRecords()) {
                if (modulePermissionVO.getModuleName().equals(monitorDevice.getModuleName())) {
                    List<ProjectLimit> projectLimits = WrappedBeanCopier.JSONArrayToList(modulePermissionVO.getProjects(), ProjectLimit.class);
                    for (ProjectLimit project : projectLimits) {
                        if (project.getProjectName().equals(monitorDevice.getProjectName())) {
                            hasPermission = true;
                            break;
                        }
                    }
                    break;
                }
            }
        }
        if (allIssues) {
            hasPermission = true;
        }
        Assert.isTrue(hasPermission, "没有权限访问数据");
        handleDeviceVO(vo, user);

        LambdaQueryWrapper<MonitorRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MonitorRecord::getDeviceId, vo.getDeviceId());
        wrapper.eq(MonitorRecord::getDeviceStatus, GlobalProperty.DEVICE_STATUS_ALERT);

        int count = monitorRecordService.count(wrapper);
        vo.setAlertCount(count);
//        System.out.println(vo);

        // 处理显示数据
        return BaseResponse.success(vo);
    }

    private void handleDeviceVO(MonitorDeviceVO vo, User user) {
        vo.setOnline(true);
        if (vo.getLastMessageTime() == null) {
            vo.setOnline(false);
        } else {
            // over 2 hours, regard the device as offline
            if (Duration.between(vo.getLastMessageTime(), LocalDateTime.now()).toHours() >= 2) {
                vo.setOnline(false);
            }
        }

        vo.setIsElectric(true);
        vo.setIsEdit(true);
        // 权限过滤
        if (!UserService.checkAuthority(user, "electricMonitor")) {


            vo.setVoltage(null);
            vo.setBattery(null);
            vo.setElectric(null);
            vo.setIsElectric(false);
        }

        if (!UserService.checkAuthority(user,"editMonitor")){
            vo.setIsEdit(false);
        }
//        System.out.println("我有实时电量权限");

        if (!UserService.checkAuthority(user, "errorMonitor")) {
            if (vo.getDeviceStatus().equals(GlobalProperty.DEVICE_STATUS_ERROR)) {
                vo.setDeviceStatus(GlobalProperty.DEVICE_STATUS_NORMAL);
            }
        }
    }


    @PostMapping("/device/page")
//    @PreAuthorize("@userService.checkAuthority('monitorDevices')")
    public BaseResponse page(@RequestBody PageParam<MonitorDeviceVO> param, Authentication authentication) {

        Page<MonitorDeviceVO> page = new Page<>(param.getPageNo(), param.getPageSize());
        MonitorDeviceVO condition = param.getCondition();
        User user = (User) authentication.getPrincipal();
        if (!"ROLE_ADMIN".equals(user.getRole()) && !UserService.checkAuthority(user, "issueView")){
            return BaseResponse.success(page);
        }

        List<String> strings = null;
        boolean allIssues = UserService.checkAuthority(user, "allIssues");
        boolean ownIssues = UserService.checkAuthority(user, "ownIssues");
        boolean projectIssues = UserService.checkAuthority(user, "projectIssues");

        if (!allIssues && !ownIssues && projectIssues) {
            strings = new ArrayList<>();
            List<ModulePermissionVO> permissions = modulePermissionService.getBaseMapper().getByUserId(user.getUserId());
            for (ModulePermissionVO permission : permissions) {
                List<ProjectLimit> projectLimits = WrappedBeanCopier.JSONArrayToList(permission.getProjects(), ProjectLimit.class);
                for (ProjectLimit projectLimit : projectLimits) {
                    if (BooleanUtils.isTrue(projectLimit.getLimit())) {
                        strings.add(permission.getModuleName() + projectLimit.getProjectName());
                    }
                }

            }
        }

        monitorDeviceService.getBaseMapper().pageMonitorDevice(condition.getModuleName(), condition.getProjectName(), condition.getAreaName(), strings, page);

//        monitorDeviceService.page(page, new LambdaQueryWrapper<MonitorDevice>()
//                .eq(StringUtils.isNotBlank(condition.getModuleName()), MonitorDevice::getModuleName, condition.getModuleName())
//                .eq(StringUtils.isNotBlank(condition.getProjectName()), MonitorDevice::getProjectName, condition.getProjectName())
//                .eq(StringUtils.isNotBlank(condition.getAreaName()), MonitorDevice::getAreaName, condition.getAreaName())
//                .eq(StringUtils.isNotBlank(condition.getSerialNumber()), MonitorDevice::getSerialNumber, condition.getSerialNumber())
//                .orderByDesc(MonitorDevice::getCreateTime)
//        );

//        System.out.println(page.getRecords());

        List<MonitorDeviceVO> monitorDeviceVOList = ListUtil.transformOtherTypeList(page.getRecords(), MonitorDeviceVO.class, (po, vo) -> {
            handleDeviceVO(vo, user);
            return vo;
        });
//        System.out.println(monitorDeviceVOList);
        page.setRecords(monitorDeviceVOList);
        return BaseResponse.success(page);
    }

    @PostMapping("/device/delete")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public BaseResponse delete(@RequestBody DeleteParam param) {
        List<String> ids = param.getIds();
        if (ids!=null && ids.size()>0){
            monitorDeviceService.removeByIds(ids);
            for (String id : ids) {
                LambdaQueryWrapper<MonitorRecord> monitorRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
                monitorRecordLambdaQueryWrapper.eq(MonitorRecord::getDeviceId,id);
                monitorRecordService.remove(monitorRecordLambdaQueryWrapper);
            }
        }

        return BaseResponse.success();
    }

    @PostMapping("/setMonitorInterval")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @AopLog(name = "设置白蚁检测时间", platform = "管理后台")
    public BaseResponse setMonitorInterval(@RequestBody SetMonitorIntervalParam param) {
        boolean update = moduleService.update(new LambdaUpdateWrapper<Module>().set(Module::getMonitorInterval, param.getMonitorInterval()).eq(Module::getModuleId, param.getModuleId()));
        Assert.isTrue(update, "设置白蚁检测间隔失败");
        Module module = moduleService.getById(param.getModuleId());
        List<MonitorDevice> list = monitorDeviceService.list(new LambdaQueryWrapper<MonitorDevice>().eq(MonitorDevice::getModuleName, module.getModuleName()));
        list.stream().map(MonitorDevice::getSerialNumber).forEach(s -> {
            SetDeviceIntervalMessageForSend message = new SetDeviceIntervalMessageForSend();
            message.setInterval(param.getMonitorInterval());
            message.setMessageId(RandomUtil.randomInt(60000));
            try {
                String topic = String.format("dev/ThermiteDet/00-01/%s/down", s);
                byte[] bytes = MqttMessageUtil.objectToBytes(message);
                client.publish(topic, bytes, 1, true);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        return BaseResponse.success();
    }

    // 报警历史记录
    @PostMapping("/device/monitorRecord")
    public BaseResponse monitorRecord(@RequestBody PageParam<MonitorRecordParam> param, Authentication authentication) {
        Page<MonitorRecord> page = new Page<>(param.getPageNo(), param.getPageSize());
        MonitorRecordParam condition = param.getCondition();
        User user = (User) authentication.getPrincipal();

        List<String> moduleProjects = monitorRecordService.getModuleProjects(user);
        Page<MonitorRecord> monitorRecordPage = monitorRecordService.getMonitorRecordPage(condition, moduleProjects, page);
        return BaseResponse.success(monitorRecordPage);
    }

    @PostMapping("/device/monitorMap")
    public BaseResponse monitorMap(@RequestBody HashMap<String, String> map, Authentication authentication) {
        String projectName = map.get("projectName");
        String moduleName = map.get("moduleName");
        User user = (User) authentication.getPrincipal();
        if (!"ROLE_ADMIN".equals(user.getRole()) && !UserService.checkAuthority(user, "issueView")){
            return BaseResponse.success(new MonitorDeviceCountVO());
        }

        List<String> moduleProjects = monitorRecordService.getModuleProjects(user);
        MonitorDeviceCountVO monitorDeviceCountVO = monitorDeviceService.countDevice(moduleName,projectName, moduleProjects, user);
        return BaseResponse.success(monitorDeviceCountVO);
    }

    @PostMapping("/device/alertCount")
    public BaseResponse alertCount(@RequestBody MonitorAlertCountParam param, Authentication authentication) {
        User user = (User) authentication.getPrincipal();
        if (!"ROLE_ADMIN".equals(user.getRole()) && !UserService.checkAuthority(user, "issueView")){
            return BaseResponse.success();
        }

        List<String> moduleProjects = monitorRecordService.getModuleProjects(user);
        Object monitorAlertCountVOS = monitorRecordService.alertCount(param, moduleProjects);
        return BaseResponse.success(monitorAlertCountVOS);
    }

    @PostMapping("/device/monitorIssues")
    public BaseResponse monitorIssues(@RequestBody PageParam<MonitorDeviceVO> param, Authentication authentication) {
        Page<IssueVO> page = new Page<>(param.getPageNo(), param.getPageSize());
        MonitorDeviceVO condition = param.getCondition();
        User user = (User) authentication.getPrincipal();

        MonitorDevice monitorDevice = monitorDeviceService.getById(condition.getDeviceId());
        Assert.isTrue(!ObjectUtil.isEmpty(monitorDevice), "id不可为空");

        List<String> moduleProjects = monitorRecordService.getModuleProjects(user);
        page = issueService.getBaseMapper().getRecordsByModule(monitorDevice.getModuleName(), monitorDevice.getProjectName(), moduleProjects, monitorDevice.getDeviceId(), page);

        return BaseResponse.success(page);
    }

    @PostMapping("/device/modules")
//    @PreAuthorize("@userService.checkAuthority('allIssues','ownIssues','projectIssues')")
//    @AopLog(name = "模块列表", platform = "管理后台")
    public BaseResponse modules(@AuthenticationPrincipal User user, @RequestBody PageParam<MonitorDeviceVO> param) {
//        Page<MonitorDevice> page = new Page<>(param.getPageNo(), param.getPageSize());
//        MonitorDeviceVO condition = param.getCondition();
//
//        List<String> moduleProjects = monitorRecordService.getModuleProjects(user);
//
//        page =  monitorDeviceService.dropDownList("module_name",condition, moduleProjects,page);
//
//        return BaseResponse.success(page);
        Page<Module> page = new Page<>(param.getPageNo(), param.getPageSize());
        boolean allIssues = UserService.checkAuthority(user, "allIssues");
        boolean ownIssues = UserService.checkAuthority(user, "ownIssues");
        boolean projectIssues = UserService.checkAuthority(user, "projectIssues");
        if (!allIssues && !ownIssues && projectIssues) {
            Page<ModuleVO> page1 = new Page<>(param.getPageNo(), param.getPageSize());
            Page<ModulePermissionVO> voPage = modulePermissionService.getBaseMapper().getByUserIdPage(user.getUserId(), page1);
            modulePermissionService.concatData(voPage.getRecords());
            return BaseResponse.success(voPage);
        }
        page = moduleService.lambdaQuery().select(Module::getModuleName, Module::getProjects).orderByAsc(Module::getModuleOrder).page(page);
        return BaseResponse.success(page);
    }

    @PostMapping("/device/projects")
    public BaseResponse projects(@AuthenticationPrincipal User user, @RequestBody PageParam<MonitorDeviceVO> param) {
        Page<MonitorDevice> page = new Page<>(param.getPageNo(), param.getPageSize());
        MonitorDeviceVO condition = param.getCondition();

        List<String> moduleProjects = monitorRecordService.getModuleProjects(user);

        page =  monitorDeviceService.dropDownList("project_name",condition, moduleProjects,page);

        return BaseResponse.success(page);
    }

    @PostMapping("/device/areas")
    public BaseResponse areas(@AuthenticationPrincipal User user, @RequestBody PageParam<MonitorDeviceVO> param) {
        Page<MonitorDevice> page = new Page<>(param.getPageNo(), param.getPageSize());
        MonitorDeviceVO condition = param.getCondition();

        List<String> moduleProjects = monitorRecordService.getModuleProjects(user);

        page =  monitorDeviceService.dropDownList("area_name",condition, moduleProjects,page);

        return BaseResponse.success(page);
    }

    @PostMapping("/device/monitorObjects")
    public BaseResponse monitorObjects(@RequestBody HashMap<String, String> map) {
        String moduleName = map.get("moduleName");
        String projectName = map.get("projectName");
        String areaName = map.get("areaName");

        LambdaQueryWrapper<MonitorRecord> monitorRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        monitorRecordLambdaQueryWrapper.eq(MonitorRecord::getProjectName, projectName);
        monitorRecordLambdaQueryWrapper.eq(MonitorRecord::getModuleName, moduleName);
        monitorRecordLambdaQueryWrapper.eq(MonitorRecord::getAreaName, areaName);
        monitorRecordLambdaQueryWrapper.groupBy(MonitorRecord::getMonitorObject);
        monitorRecordLambdaQueryWrapper.select(MonitorRecord::getMonitorObject);

        List<MonitorRecord> monitorRecords = monitorRecordService.list(monitorRecordLambdaQueryWrapper);

        return BaseResponse.success(monitorRecords);
    }

    private void validateDeviceData(String moduleName, String projectName, String areaName) {
        Module module = moduleService.getOne(new LambdaQueryWrapper<Module>().eq(Module::getModuleName, moduleName), false);
        Assert.isTrue(module != null, "模块不存在");

        List<Project> projects = WrappedBeanCopier.JSONArrayToList(module.getProjects(), Project.class);
        boolean existProject = false;
        for (Project project : projects) {
            if (project.getProjectName().equals(projectName)) {
                existProject = true;
                break;
            }
        }
        Assert.isTrue(existProject, "项目不存在");

//        List<String> areas = issueService.listObjs(new LambdaQueryWrapper<Issue>()
//                        .select(Issue::getIssueArea)
//                        .eq(Issue::getModuleName, moduleName)
//                        .eq(Issue::getProjectName, projectName)
//                        .orderByDesc(Issue::getReportTime)
//                , Object::toString);
//        Assert.isTrue(areas.contains(areaName), "区域不存在");
    }
    @PostMapping("/device/auditRecord")
    public R auditRecord(String recordId,Integer auditStatus){
        MonitorRecord monitorRecord = monitorRecordService.getById(recordId);
        monitorRecord.setAuditStatus(auditStatus);
        monitorRecordService.save(monitorRecord);
        return R.ok("审核成功");
    }
    @AopLog(saveLog = false, name = "查询客户分页", platform = "管理后台")
    @PostMapping("/device/getRecordPage")
    @AllowVisitor(checkToken = false)
    public BaseResponse getRecordPage(@RequestBody MonitorRecordVO param) {
        Page<MonitorRecord> page = new Page<>(param.getPageNo(), param.getPageSize());
        page = monitorRecordService.getBaseMapper().getRecordPage(param.getModuleName(),param.getProjectName(), param.getAreaName(),
                param.getSerialNumber(), page);
        return BaseResponse.success(page);
    }

    @GetMapping("/device/getRecordById")
    @AllowVisitor(checkToken = false)
    public  BaseResponse getRecordById(String recordId){
        MonitorRecord monitorRecord = monitorRecordService.getBaseMapper().getRecordById(recordId);
        return BaseResponse.success(monitorRecord);
    }


}
